import { exec, execFile } from "child_process"
import { existsSync, promises as fs } from "fs"
import path from "path"
import { rimraf } from "rimraf"
import { registrySchema } from "shadcn/schema"

import { getAllBlocks } from "@/lib/blocks"
import { STYLES, type Style } from "@/registry/styles"

async function buildRegistryIndex(styles: Style[]) {
  let index = `/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-explicit-any */
// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
import * as React from "react"

export const Index: Record<string, Record<string, any>> = {`

  for (const style of styles) {
    // Dynamically import the registry for this style.
    const { registry: importedRegistry } = await import(
      `../registry/${style.name}/registry.ts`
    )

    // Validate the registry schema.
    const parseResult = registrySchema.safeParse(importedRegistry)
    if (!parseResult.success) {
      console.error(`❌ Registry validation failed for ${style.name}:`)
      console.error(parseResult.error.format())
      throw new Error(`Invalid registry schema for ${style.name}`)
    }

    const registry = parseResult.data

    index += `
  "${style.name}": {`

    for (const item of registry.items) {
      const files =
        item.files?.map((file) => ({
          path: typeof file === "string" ? file : file.path,
          type: typeof file === "string" ? item.type : file.type,
          target: typeof file === "string" ? undefined : file.target,
        })) ?? []

      if (files.length === 0) {
        continue
      }

      const componentPath = item.files?.[0]?.path
        ? `@/registry/${style.name}/${item.files[0].path}`
        : ""

      index += `
    "${item.name}": {
      name: "${item.name}",
      description: "${item.description ?? ""}",
      type: "${item.type}",
      registryDependencies: ${JSON.stringify(item.registryDependencies)},
      files: [${files.map((file) => {
        const filePath = `registry/${style.name}/${file.path}`
        return `{
        path: "${filePath}",
        type: "${file.type}",
        target: "${file.target ?? ""}"
      }`
      })}],
      component: ${
        componentPath
          ? `React.lazy(async () => {
        const mod = await import("${componentPath}")
        const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') || item.name
        return { default: mod.default || mod[exportName] }
      })`
          : "null"
      },
      categories: ${JSON.stringify(item.categories)},
      meta: ${JSON.stringify(item.meta)},
    },`
    }

    index += `
  },`
  }

  index += `
}`

  console.log(
    `#️⃣  Built multi-style index with ${styles.length} styles: ${styles.map((s) => s.name).join(", ")}`
  )

  // Write unified index.
  rimraf.sync(path.join(process.cwd(), "registry/__index__.tsx"))
  await fs.writeFile(path.join(process.cwd(), "registry/__index__.tsx"), index)
}

async function buildRegistryJsonFile(styleName: string) {
  // 1. Import the registry for this style.
  const { registry: importedRegistry } = await import(
    `../registry/${styleName}/registry.ts`
  )

  // 2. Validate the registry schema.
  const parseResult = registrySchema.safeParse(importedRegistry)
  if (!parseResult.success) {
    console.error(`❌ Registry validation failed for ${styleName}:`)
    console.error(parseResult.error.format())
    throw new Error(`Invalid registry schema for ${styleName}`)
  }

  const registry = parseResult.data

  // 3. Fix the path for registry items.
  const fixedRegistry = {
    ...registry,
    items: registry.items.map((item) => {
      const files = item.files?.map((file) => {
        return {
          ...file,
          path: `registry/${styleName}/${file.path}`,
        }
      })

      return {
        ...item,
        files,
      }
    }),
  }

  // 3. Create the output directory and write registry.json.
  const outputDir = path.join(
    process.cwd(),
    styleName === "new-york-v4" ? `public/r/styles/${styleName}` : `public/r/${styleName}`
  )
  await fs.mkdir(outputDir, { recursive: true })

  // 4. Write registry.json to output directory and format it.
  const registryJsonPath = path.join(outputDir, "registry.json")
  await fs.writeFile(registryJsonPath, JSON.stringify(fixedRegistry, null, 2))
  await new Promise<void>((resolve, reject) => {
    execFile('prettier', ['--write', registryJsonPath], (error) => {
      if (error) {
        reject(error);
      } else {
        resolve();
      }
    });
  })

  // 5. Write temporary registry file needed by shadcn build.
  const tempRegistryPath = path.join(process.cwd(), `registry-${styleName}.json`)
  await fs.writeFile(tempRegistryPath, JSON.stringify(fixedRegistry, null, 2))
}

async function buildRegistry(styleName: string) {
  return new Promise((resolve, reject) => {
    // Use local shadcn copy.
    const outputPath =
      styleName === "new-york-v4" ? `public/r/styles/${styleName}` : `public/r/${styleName}`
    const process = exec(
      `node ../../packages/shadcn/dist/index.js build registry-${styleName}.json --output ${outputPath}`
    )

    // exec(
    //   `pnpm dlx shadcn build registry-${styleName}.json --output public/r/styles/${styleName}`
    // )

    process.on("exit", (code) => {
      if (code === 0) {
        resolve(undefined)
      } else {
        reject(new Error(`Process exited with code ${code}`))
      }
    })
  })
}

async function buildBlocksIndex() {
  const blocks = await getAllBlocks(["registry:block"])

  const payload = blocks.map((block) => ({
    name: block.name,
    description: block.description,
    categories: block.categories,
  }))

  rimraf.sync(path.join(process.cwd(), "registry/__blocks__.json"))
  await fs.writeFile(
    path.join(process.cwd(), "registry/__blocks__.json"),
    JSON.stringify(payload, null, 2)
  )

  await exec(`prettier --write registry/__blocks__.json`)
}

try {
  const styles = Array.from(STYLES)
  console.log(`🎨 Found ${styles.length} styles: ${styles.map((s) => s.name).join(", ")}`)

  // Build unified multi-style index.
  console.log("\n🗂️ Building unified multi-style registry/__index__.tsx...")
  await buildRegistryIndex(styles)

  for (const style of styles) {
    console.log(`\n📦 Processing style: ${style.name}`)

    console.log(`💅 Building registry-${style.name}.json...`)
    await buildRegistryJsonFile(style.name)

    console.log(`🏗️ Building registry for ${style.name}...`)
    await buildRegistry(style.name)
  }

  console.log("\n🗂️ Building registry/__blocks__.json...")
  await buildBlocksIndex()

  // Clean up intermediate files.
  console.log("\n🧹 Cleaning up intermediate files...")
  for (const style of styles) {
    if (existsSync(path.join(process.cwd(), `registry-${style.name}.json`))) {
      await fs.unlink(path.join(process.cwd(), `registry-${style.name}.json`))
    }
  }

  console.log("\n✅ Build complete!")
} catch (error) {
  console.error(error)
  process.exit(1)
}
